using System;
using System.Linq;
using System.Threading.Tasks;
using CodeAngel.Domain.Framework;

namespace CodeAngel.Domain.Aggregates
{
    /// <summary>
    /// An aggregate that contains all necessary data to edit a domain
    /// </summary>
	public partial class DomainFullAggregate : Aggregate
	{

		#region Root

		public new Domain Root
		{
		    get
		    {
                return base.Root as Domain;
		    }
		}

		#endregion

		#region CreateNew

        /// <summary>
        /// Creates the root of this aggreate as a new entity.
        /// </summary>
        /// <remarks>
        /// The currently logged in user will be used for security purposes.
        /// </remarks>
		public void CreateNew()
		{
            this.CreateNew(User.LoggedInUser);
		}

        /// <summary>
        /// Creates the root of this aggreate as a new entity/collection.
        /// </summary>
		public void CreateNew(User user)
		{
            var ThisRoot = new Domain(this);
            this.User = user;
            ThisRoot.IsNew = true;
            ThisRoot.InitializeAllProperties();
            ThisRoot.SetDefaultPropertyValues();
            ThisRoot.BuildObjectGraphCompleted();
            this.DtoHost = new DtoHost();
            base.Root = ThisRoot;
		}

		#endregion

		#region LoadAsync

        /// <summary>
        /// Instructs this aggregate to asynchronously load its object graph based on the
        /// identified id.  The currently logged in user (see User.LoggedInUser) will be used for
        /// security related purposes.
        /// </summary>
		public Task LoadAsync(Guid id)
		{
            return this.LoadAsync(new DomainFilter(id), User.LoggedInUser);
		}

		#endregion

		#region IsFilterTypeValid

        /// <summary>
        /// Determines if the passed in filter is valid for this aggregate.
        /// </summary>
		protected override bool IsFilterTypeValid(IFilter filter)
		{
            return (filter is DomainFilter);
		}

		#endregion

		#region BuildObjectGraph

        /// <summary>
        /// Provides a way for the developer to custom build the object graph for this aggregate. 
        /// Be sure to set the _BuildObjectGraphIsHandled field to true to avoid having the
        /// auto-generated BuildObjectGraph code executed.
        /// </summary>
		partial void CustomBuildObjectGraph();

        /// <summary>
        /// Informs the auto-generated BuildObjectGraph method that building the object graph has
        /// already been handled and it does not need to do anything.
        /// </summary>
		private bool _BuildObjectGraphIsHandled = false;

        /// <summary>
        ///
        /// </summary>
		protected override void BuildObjectGraph()
		{
            // Offers the developer a way to build the object graph by hand.
            this.CustomBuildObjectGraph();
            if (this._BuildObjectGraphIsHandled) return;
            
            try
            {
            var ThisRoot = new Domain(this);
            var ThisDomainDto = this.DtoHost.RootDomains[0];
            ThisRoot.InitializeAllScalarProperties(ThisDomainDto);
            
            var DevelopmentDatabaseDto1 = this.DtoHost.Databases.Find<DatabaseDto>(ThisDomainDto.DevelopmentDatabaseID);
            if (DevelopmentDatabaseDto1 != null)
            {
                var DevelopmentDatabase1 = new Database(this, ThisRoot);
                ThisRoot.InitializeDevelopmentDatabaseProperty(DevelopmentDatabase1);
            DevelopmentDatabase1.InitializeAllScalarProperties(DevelopmentDatabaseDto1);
            }
            else
            {
                ThisRoot.InitializeDevelopmentDatabaseProperty();
            }
            
            var ProductionDatabaseDto2 = this.DtoHost.Databases.Find<DatabaseDto>(ThisDomainDto.ProductionDatabaseID);
            if (ProductionDatabaseDto2 != null)
            {
                var ProductionDatabase2 = new Database(this, ThisRoot);
                ThisRoot.InitializeProductionDatabaseProperty(ProductionDatabase2);
            ProductionDatabase2.InitializeAllScalarProperties(ProductionDatabaseDto2);
            }
            else
            {
                ThisRoot.InitializeProductionDatabaseProperty();
            }
            
            ThisRoot.InitializeEntityDefsProperty();
            foreach (var EntityDefDto3 in this.DtoHost.EntityDefs.FindAll(ThisDto => ThisDto.DomainID.Equals(ThisDomainDto.ID)))
            {
            var EntityDef3 = new EntityDef(this);
            ThisRoot.EntityDefs.Add(EntityDef3);
            EntityDef3.InitializeDomainProperty(ThisRoot);
            EntityDef3.InitializeAllScalarProperties(EntityDefDto3);
            
            var FilterRelationDto4 = this.DtoHost.FilterRelations.Find<FilterRelationDto>(EntityDefDto3.FilterRelationID);
            if (FilterRelationDto4 != null)
            {
                var FilterRelation4 = new FilterRelation(this, EntityDef3);
                EntityDef3.InitializeFilterRelationProperty(FilterRelation4);
            FilterRelation4.InitializeAllScalarProperties(FilterRelationDto4);
            
            FilterRelation4.InitializeFilterRelationsProperty();
            foreach (var FilterRelationDto5 in this.DtoHost.FilterRelations.FindAll(ThisDto => ThisDto.ParentFilterRelationID.Equals(FilterRelationDto4.ID)))
            {
            var FilterRelation5 = new FilterRelation(this);
            FilterRelation4.FilterRelations.Add(FilterRelation5);
            FilterRelation5.InitializeParentFilterRelationProperty(FilterRelation4);
            FilterRelation5.InitializeAllScalarProperties(FilterRelationDto5);
            }
            }
            else
            {
                EntityDef3.InitializeFilterRelationProperty();
            }
            
            EntityDef3.InitializeAggregateDefsProperty();
            foreach (var AggregateDefDto6 in this.DtoHost.AggregateDefs.FindAll(ThisDto => ThisDto.EntityDefID.Equals(EntityDefDto3.ID)))
            {
            var AggregateDef6 = new AggregateDef(this);
            EntityDef3.AggregateDefs.Add(AggregateDef6);
            AggregateDef6.InitializeEntityDefProperty(EntityDef3);
            AggregateDef6.InitializeAllScalarProperties(AggregateDefDto6);
            
            var DataElementDto7 = this.DtoHost.DataElements.Find<DataElementDto>(AggregateDefDto6.DataElementID);
            if (DataElementDto7 != null)
            {
                var DataElement7 = new DataElement(this, AggregateDef6);
                AggregateDef6.InitializeDataElementProperty(DataElement7);
            DataElement7.InitializeAllScalarProperties(DataElementDto7);
            }
            else
            {
                AggregateDef6.InitializeDataElementProperty();
            }
            }
            
            EntityDef3.InitializePropertyDefsProperty();
            foreach (var PropertyDefDto8 in this.DtoHost.PropertyDefs.FindAll(ThisDto => ThisDto.EntityDefID.Equals(EntityDefDto3.ID)))
            {
            var PropertyDef8 = new PropertyDef(this);
            EntityDef3.PropertyDefs.Add(PropertyDef8);
            PropertyDef8.InitializeEntityDefProperty(EntityDef3);
            PropertyDef8.InitializeAllScalarProperties(PropertyDefDto8);
            
            var ForeignKeyPropertyDefDto9 = this.DtoHost.PropertyDefs.Find<PropertyDefDto>(PropertyDefDto8.ForeignKeyPropertyDefID);
            if (ForeignKeyPropertyDefDto9 != null)
            {
                var ForeignKeyPropertyDef9 = new PropertyDef(this, PropertyDef8);
                PropertyDef8.InitializeForeignKeyPropertyDefProperty(ForeignKeyPropertyDef9);
            ForeignKeyPropertyDef9.InitializeAllScalarProperties(ForeignKeyPropertyDefDto9);
            }
            else
            {
                PropertyDef8.InitializeForeignKeyPropertyDefProperty();
            }
            }
            }
            base.Root = ThisRoot;
            }
            catch (Exception ex)
            {
            throw new Exception("There was an unexpected problem building the object graph.  See inner exception for more info.", ex);
            }
		}

		#endregion

		#region SynchronizeDtos

        /// <summary>
        /// Provides a way for the developer to custom synchronize the dtos with the object graph
        /// for this aggregate.  Be sure to set the _SynchronizeDtosIsHandled field to true to
        /// avoid having the auto-generated synchronization code executed.
        /// </summary>
		partial void CustomSynchronizeDtos();

        /// <summary>
        /// Informs the auto-generated SynchronizeDtos method that synchronization has already been
        /// handled and it does not need to do anything.
        /// </summary>
		private bool _SynchronizeDtosIsHandled = false;

        /// <summary>
        ///
        /// </summary>
		protected override void SynchronizeDtos()
		{
            // Offers the developer a way to synchronize the dtos by hand.
            this.CustomSynchronizeDtos();
            if (this._SynchronizeDtosIsHandled) return;
            
            try
            {
            this.Root.SynchronizeDto(this.DtoHost.RootDomains);
            var Database10 = this.Root.DevelopmentDatabase;
            if (Database10 != null)
            {
                Database10.SynchronizeDto(this.DtoHost.Databases);
            }
            var Database11 = this.Root.ProductionDatabase;
            if (Database11 != null)
            {
                Database11.SynchronizeDto(this.DtoHost.Databases);
            }
            foreach (var EntityDef12 in this.Root.EntityDefs)
            {
                EntityDef12.SynchronizeDto(this.DtoHost.EntityDefs);
            var FilterRelation13 = EntityDef12.FilterRelation;
            if (FilterRelation13 != null)
            {
                FilterRelation13.SynchronizeDto(this.DtoHost.FilterRelations);
            foreach (var FilterRelation14 in FilterRelation13.FilterRelations)
            {
                FilterRelation14.SynchronizeDto(this.DtoHost.FilterRelations);
            }
            }
            foreach (var AggregateDef15 in EntityDef12.AggregateDefs)
            {
                AggregateDef15.SynchronizeDto(this.DtoHost.AggregateDefs);
            var DataElement16 = AggregateDef15.DataElement;
            if (DataElement16 != null)
            {
                DataElement16.SynchronizeDto(this.DtoHost.DataElements);
            }
            }
            foreach (var PropertyDef17 in EntityDef12.PropertyDefs)
            {
                PropertyDef17.SynchronizeDto(this.DtoHost.PropertyDefs);
            var PropertyDef18 = PropertyDef17.ForeignKeyPropertyDef;
            if (PropertyDef18 != null)
            {
                PropertyDef18.SynchronizeDto(this.DtoHost.PropertyDefs);
            }
            }
            }
            }
                catch (Exception ex)
            {
                throw new Exception("There was an unexpected problem while synchronizing DTOs.  See inner exception for more info.", ex);
            }
		}

		#endregion

		#region UserHasPermissionToSave

        /// <summary>
        /// Identifies if the current user has permission to save all the updateable entities in
        /// this AggregateDefinition.
        /// </summary>
		protected override bool UserHasPermissionToSave()
		{
            return 
                this.User.CanUpdateDomain() &&
                this.User.CanUpdateEntityDef() &&
                this.User.CanUpdateFilterRelation() &&
                this.User.CanUpdateFilterRelation() &&
                this.User.CanUpdateAggregateDef() &&
                this.User.CanUpdateDataElement() &&
                this.User.CanUpdatePropertyDef() &&
                this.User.CanUpdatePropertyDef() &&
                this.User.CanUpdateDatabase() &&
                this.User.CanUpdateDatabase();
		}

		#endregion

	}
}

